﻿using System.Collections.Generic;
using Engine.Animations;
using Microsoft.Xna.Framework;

namespace Engine.AI.Astar
{
    public class BoxableItemAstarState : State
    {
        #region Constructors

        public BoxableItemAstarState(IBoxableAstarStateAware item, World world, Vector3 pos, BoxableItemAstarState parent, BoxableItemAstarState goal)
        {
            _item = item;
            MaxError = 10;
            _world = world;
            Parent = parent;
            _goal = goal;
            Position = pos;
            if (parent != null)
                _height = parent._height + 1;
        }

        #endregion

        #region Fields
		 	
        readonly private World _world;
        readonly private IBoxableAstarStateAware _item;
        readonly private BoxableItemAstarState _goal;
        private float _weight;

        #endregion
                
        #region Properties

        public override float Weight
        {
            get { return _weight; }
        }
		 
	    #endregion

        #region Public Methods
		 	
        public override void ComputeWeight()
        {
            Vector3 pos1 = new Vector3(Position.X, 0, Position.Z);
            Vector3 pos2 = new Vector3(_goal.Position.X, 0, _goal.Position.Z);
            _weight = Height + Vector3.Distance(pos1, pos2);

        }

        public Vector3 Position { get; set; }

        public override List<State> GenerateChilds(HashSet<State> visitedPoints)
        {
            List<State> newStates = new List<State>();

            const float offset = 10f;
            AddState(Position + Vector3.UnitX * offset, newStates, visitedPoints);
            AddState(Position - Vector3.UnitX * offset, newStates, visitedPoints);
            AddState(Position + Vector3.UnitZ * offset, newStates, visitedPoints);
            AddState(Position - Vector3.UnitZ * offset, newStates, visitedPoints);

            AddState(Position + new Vector3(offset, 0, offset), newStates, visitedPoints);
            AddState(Position - new Vector3(offset, 0, offset), newStates, visitedPoints);
            AddState(Position + new Vector3(offset, 0, -offset), newStates, visitedPoints);
            AddState(Position + new Vector3(-offset, 0, offset), newStates, visitedPoints);


            return newStates;
        }
        public Vector3? MovePlayer(Vector3? newPos)
        {
            Vector3? validatedNewPos = null;
            if (newPos != null && _world.Terrain.IsOnHeightmap(newPos.Value))
            {
                float newHeight = _world.Terrain.GetHeight(newPos.Value);
                validatedNewPos=new Vector3(newPos.Value.X,newHeight,newPos.Value.Z);

                BoundingBox newBoundingBox = _item.InitialBoundingBox;
                newBoundingBox.Min += validatedNewPos.Value;
                newBoundingBox.Max += validatedNewPos.Value;
            
                if (_world.Interescts(newBoundingBox) != null)
                    return null;
            }
            return validatedNewPos;
        }
        public override bool Equals(object obj)
        {
            BoxableItemAstarState state = (BoxableItemAstarState)obj;

            if (Position == state.Position)
                return true;

            return false;
        }
        public override int GetHashCode()
        {
            return Position.ToString().GetHashCode();
        }
        public override bool IsGoal(State state)
        {
            Vector3 pos1 = new Vector3(Position.X, 0, Position.Z);
            Vector3 pos2 = new Vector3(((BoxableItemAstarState)state).Position.X, 0, ((BoxableItemAstarState)state).Position.Z);
            if (Vector3.Distance(pos1, pos2) < MaxError)
                return true;
            
            return false;
        }

        #endregion

        #region Private Methods
        
        private void AddState(Vector3 newPos, List<State> newStates, HashSet<State> visitedPoints)
        {
            BoxableItemAstarState newState = new BoxableItemAstarState(_item, _world, newPos, this, _goal);
           
            if (visitedPoints.Add(newState))
            {                
                Vector3? newPlayerPos = MovePlayer( newPos);
                if (newPlayerPos != null)
                {
                    newState.Position = newPlayerPos.Value;
                    newState.MaxError = MaxError;
                    newState.ComputeWeight();
                    newStates.Add(newState);
                }
            }
        }

        #endregion                 
    }
}
